/*******************************************************************************
 * Copyright (c) 2010 Gregory Smith (gsmithfarmer@gmail.com)
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 *******************************************************************************/

package com.aslan.sfdc.partner;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.aslan.sfdc.partner.LoginCredentials;

/**
 * Maintain named credentials that can be used to connect to salesforce.
 * 
 * When the registry is created it will, by default, look for salesforce credential
 * definitions in $user.home/sqlforce.ini. If it exists, sqlforce.ini should be an XML
 * file in the form:
 * <pre>
  		&lt;sqlforce&gt;
            &lt;connection 
                name="Production"
                type="Production"
                username="gsmithfarmer@gmail.com"
                password="yourPassword"
                token="yourSecurityToken"
            /&gt;
        &lt;/sqlforce&gt;
 * </pre>
 * <p>
 * Where:
 * <ul>
 * <li>name - logical name for the credentials
 * <li>type - production or sandbox
 * <li>username - salesforce username
 * <li>password - salesforce password
 * <li>token - security token generated by salesforce.
 * </ul>
 * @author gsmithfarmer@gmail.com
 *
 */
public class LoginCredentialsRegistry {

	private static LoginCredentialsRegistry INSTANCE;
	private Map<String,Credentials> credentialsMap = new HashMap<String,Credentials>();
	
	private class Credentials {
		private String name;
		private LoginCredentials credentials;
		
		Credentials( String name, LoginCredentials credentials ) {
			this.name = name;
			this.credentials = credentials;
		}
	}
	/**
	 * SAX Parser handler for picking up salesforce credentials from a configuration file.
	 * 
	 * @author greg
	 *
	 */
	private class CredentialsHandler extends DefaultHandler
	{
		private LoginCredentialsRegistry registry;
		
		CredentialsHandler(LoginCredentialsRegistry registry ) {
			this.registry = registry;
		}

		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			
			if( !"connection".equals( qName )) { return; }
			
			String name = attributes.getValue( "name");
			String type = attributes.getValue( "type");
			String username = attributes.getValue( "username");
			String password = attributes.getValue( "password");
			String securityToken = attributes.getValue( "token");
			
			LoginCredentials.ConnectionType cType = LoginCredentials.ConnectionType.Production;
			if( "production".equalsIgnoreCase(type)) {
				cType = LoginCredentials.ConnectionType.Production;
			} else if( "sandbox".equalsIgnoreCase(type)) {
				cType = LoginCredentials.ConnectionType.Sandbox;
			}
			
			LoginCredentials cred = new LoginCredentials( cType, username, password, securityToken );
			registry.addCredentials( name, cred );
		}
		
		
	}
	private LoginCredentialsRegistry() {
		
		String userHome = System.getProperty("user.home");
		File profile = new File( userHome + File.separator + "sqlforce.ini");
		
		if( profile.exists() && profile.isFile()) {
			try {
				loadCredentials( profile );
			} catch( Exception e ) {
				throw new Error( e.getMessage());
			}
		}
	
	}
	
	/**
	 * Return the one and only instance of the registry.
	 * 
	 * @return the registry.
	 */
	public static LoginCredentialsRegistry getInstance() {
		if( null == INSTANCE ) {
			INSTANCE = new LoginCredentialsRegistry();
		}
		
		return INSTANCE;
	}
	
	/**
	 * Register new credentials with the system (overwrite any credentials with the same name).
	 * 
	 * @param name unique (case-insensitive) name for the credentials.
	 * @param credentials login credentials.
	 */
	public void addCredentials( String name, LoginCredentials credentials ) {
		credentialsMap.put( name.toUpperCase(), new Credentials(name,credentials));
	}
	
	/**
	 * Find the credentials associated with a name (case insensitive).
	 * 
	 * @param name look for these credentials.
	 * @return the credentials if found, else null.
	 */
	public LoginCredentials getCredentials( String name ) {
		Credentials credentials =  credentialsMap.get(name.toUpperCase());
		
		return null==credentials?null:credentials.credentials;
	}
	
	
	/**
	 * Return a list of all credentials known by the registry.
	 * 
	 * @return list of all known credential names.
	 */
	public List<String> getCredentialNames() {
		List<String> list = new ArrayList<String>();
		
		for( Entry<String,Credentials> entry : credentialsMap.entrySet()) {
			list.add( entry.getValue().name );
		}
		
		return list;
	}
	/**
	 * Load login credentials defined in a file into the registry.
	 * 
	 * @param file login credentials.
	 * @throws Exception if we fail to load the credentials.
	 */
	public void loadCredentials( File file ) throws Exception {
		
		SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
		CredentialsHandler handler = new CredentialsHandler(this);
		
		parser.parse( file, handler);
	}
	
}
